﻿using System;
using System.Linq;
using ClassLibrary1;
using DependencyAnalysis.DataModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTests
{
    [TestClass]
    public class AnalyzerTest : AnalyzerTestBase
    {
        [TestMethod]
        public void WhenMethodImplementsInterfaceThenMethodIsMarked()
        {
            var method = (from x in Model.DependenciesPerMember.Keys where x.FullName == "ClassLibrary1.Class1.§Void Wroom()" select x).FirstOrDefault() as MemberDescriptor;
            Assert.IsTrue(method.IsDefinedByInterface);
        }

        [TestMethod]
        public void WhenMethodDoesntImplementInterfaceThenMethodIsNotMarked()
        {
            var method = (from x in Model.DependenciesPerMember.Keys where x.FullName == "ClassLibrary1.Class1.§Void Woosh()" select x).FirstOrDefault() as MemberDescriptor;
            Assert.IsFalse(method.IsDefinedByInterface);
        }

        [TestMethod]
        public void SubclassesAreFound()
        {
            var type = (from x in Model.DependenciesPerType.Keys where x.FullName == "ClassLibrary1.Class0" select x).FirstOrDefault() as TypeDescriptor;
            Assert.AreEqual(1, type.ImplementingTypes.Count);
        }

        [TestMethod]
        public void SuperclassesAreFound()
        {
            var type = (from x in Model.DependenciesPerType.Keys where x.FullName == "ClassLibrary1.Class1" select x).FirstOrDefault() as TypeDescriptor;
            Assert.AreEqual(2, type.InheritedTypes.Count);
        }

        [TestMethod]
        public void DelegateIsRecognized()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.Class1.§Void HandleBobEvent(System.Object, System.EventArgs)" select x).FirstOrDefault();
            var delegateReference = dependencies.Value.First();
            Assert.AreEqual("ClassLibrary1.Class1.§Void Woosh()", delegateReference.Source.FullName);
        }

        [TestMethod]
        public void ConstructorIsEvaluated()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.Class1.§Void Wroom()" select x).FirstOrDefault();
            var delegateReference = dependencies.Value.First();
            Assert.AreEqual("ClassLibrary1.Class1.§Void .ctor()", delegateReference.Source.FullName);
        }

        [TestMethod]
        public void MethodsWithGenericReturnTypeAreCollapseToGeneric()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.GenericClass.§T MakeT()" select x).FirstOrDefault();
            var delegateReference = dependencies.Value.First();
            Assert.AreEqual("ClassLibrary1.GenericClass.§Void StaticReferences()", delegateReference.Source.FullName);
        }

        [TestMethod]
        public void MethodsWithGenericParameterAreCollapseToGeneric()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.GenericClass.§Void DoSomething(T)" select x).FirstOrDefault();
            var delegateReference = dependencies.Value.First();
            Assert.AreEqual("ClassLibrary1.GenericClass.§Void StaticReferences()", delegateReference.Source.FullName);
        }

        [TestMethod]
        public void StaticMemberIsMarkedStatic()
        {
            var member = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.GenericClass.§Void StaticReferences()" select x.Key).FirstOrDefault() as MemberDescriptor;
            Assert.IsTrue(member.IsStatic);
        }

        [TestMethod]
        public void ReferenceFromConstructorToStaticMethodIsDetected()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.Class1.§Void Wahoo()" select x).FirstOrDefault();
            Assert.AreEqual(1, dependencies.Value.Count);
        }

        [TestMethod]
        public void InstanceMemberIsMarkedNotStatic()
        {
            var member = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.GenericClass.§Void DoSomething(T)" select x.Key).FirstOrDefault() as MemberDescriptor;
            Assert.IsFalse(member.IsStatic);
        }

        [TestMethod]
        public void AnonymousMethodsAreDetectedAsReferences()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.AnonymousTypes.§Boolean Foo(System.String)" select x).FirstOrDefault();
            Assert.AreEqual(1, dependencies.Value.Count);
        }

        [TestMethod]
        public void GeneratedClassForYieldIsPartOfOriginalClass()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName.Contains("ClassLibrary1.AnonymousTypes+<Yield>d__7.§Void .ctor(Int32)") select x).FirstOrDefault();
            Assert.AreEqual("ClassLibrary1.AnonymousTypes", ((TypeDescriptor)dependencies.Key.Parent).OriginatingType.FullName);
        }

        [TestMethod]
        public void GeneratedClassForYieldIsAccessByOriginalMethodOnly()
        {
            var dependencies = (from x in Model.DependenciesPerType where x.Key.FullName.Contains("ClassLibrary1.AnonymousTypes+<Yield>") select x).FirstOrDefault();
            Assert.AreEqual(0, dependencies.Value.Count(x => !Equals(x.Source.FullName, "ClassLibrary1.AnonymousTypes.§System.Collections.Generic.IEnumerable`1[System.Char] Yield(System.String)")));
        }

        [TestMethod]
        public void AnonymousMethdosForDelegatesAreDetectedAsReferences()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.AnonymousTypes.§Void Delegate()" select x).FirstOrDefault();
            Assert.AreEqual(1, dependencies.Value.Count);
        }

        [TestMethod]
        public void GeneratedCodeAttributeMarksTypeAsGenerated()
        {
            var type = (from x in Model.DependenciesPerType where x.Key.FullName == "ClassLibrary1.GeneratedCodeClass" select x.Key).FirstOrDefault() as TypeDescriptor;
            Assert.IsTrue(type.IsGenerated);
        }

        [TestMethod]
        public void GeneratedCodeAttributeMarksMemberAsGenerated()
        {
            var member = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.GeneratedCodeClass.§Void GeneratedMethod()" select x.Key).FirstOrDefault() as MemberDescriptor;
            Assert.IsTrue(member.IsGenerated);
        }

        [TestMethod]
        public void AnonymousInnerTypesArePartOfDeclaringType()
        {
            var typeDescriptor = DescriptorBuilder.BuildTypeDescriptor(typeof(AnonymousTypes));
            Assert.AreEqual(1, typeDescriptor.Submodule.Types.Count(x => x.Name.Contains("AnonymousTypes")));
        }

        [TestMethod]
        public void InheritanceCountsAsTypeReference()
        {
            var typeDescriptor = DescriptorBuilder.BuildTypeDescriptor(typeof(BaseClass));
            var inheritedDescriptor = DescriptorBuilder.BuildTypeDescriptor(typeof(InheritedClass));
            var dependencies = (from x in Model.DependenciesPerType where Equals(x.Key, typeDescriptor) select x).FirstOrDefault();
            var dependency = dependencies.Value.First();
            Assert.AreEqual(inheritedDescriptor, dependency.Source.ImplementingType);
        }

        [TestMethod]
        public void ImplementationCountsAsTypeReference()
        {
            var typeDescriptor = DescriptorBuilder.BuildTypeDescriptor(typeof(IBaseInterface));
            var inheritedDescriptor = DescriptorBuilder.BuildTypeDescriptor(typeof(InheritedClass));
            var dependencies = (from x in Model.DependenciesPerType where Equals(x.Key, typeDescriptor) select x).FirstOrDefault();
            var dependency = dependencies.Value.First();
            Assert.AreEqual(inheritedDescriptor, dependency.Source.ImplementingType);
        }

        [TestMethod]
        public void ThrowCreatesTypeReferenceToException()
        {
            var class4 = DescriptorBuilder.BuildTypeDescriptor(typeof(Class4));
            var member = class4.Members.First(x => x.Name == "ThrowArgumentException");
            var exceptionType = DescriptorBuilder.BuildTypeDescriptor(typeof(ArgumentException));
            var dependency = Model.DependenciesPerMemberOut[member].FirstOrDefault(x => Equals(x.Target.ImplementingType, exceptionType));
            Assert.IsNotNull(dependency);
        }

        [TestMethod]
        public void ThrowCreatesReferenceFromCallingMember()
        {
            var class4 = DescriptorBuilder.BuildTypeDescriptor(typeof(Class4));
            var exceptionType = DescriptorBuilder.BuildTypeDescriptor(typeof(CustomException));
            var dependency = Model.DependenciesPerType[exceptionType].FirstOrDefault(x => Equals(x.Source.ImplementingType, class4));
            Assert.IsNotNull(dependency);
        }

        [TestMethod]
        public void CatchCreatesTypeReferenceToException()
        {
            var class4 = DescriptorBuilder.BuildTypeDescriptor(typeof(Class4));
            var member = class4.Members.First(x => x.Name == "CatchCustomException");
            var exceptionType = DescriptorBuilder.BuildTypeDescriptor(typeof(CustomException));
            var dependency = Model.DependenciesPerMemberOut[member].FirstOrDefault(x => Equals(x.Target.ImplementingType, exceptionType));
            Assert.IsNotNull(dependency);
        }

        [TestMethod]
        public void VisibilityForPublicEventHandlerIsRecognized()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.Events.§System.EventHandler SystemChangedPublic" select x).FirstOrDefault();
            var delegateReference = dependencies.Value.First();
            Assert.AreEqual(NodeVisibility.Public, delegateReference.Target.Visibility);
        }

        [TestMethod]
        public void VisibilityForPrivateEventHandlerIsRecognized()
        {
            var dependencies = (from x in Model.DependenciesPerMember where x.Key.FullName == "ClassLibrary1.Events.§System.EventHandler SystemChangedPrivate" select x).FirstOrDefault();
            var delegateReference = dependencies.Value.First();
            Assert.AreEqual(NodeVisibility.Private, delegateReference.Target.Visibility);
        }

        [Ignore]
        [TestMethod]
        public void GenericAttributeCreatesTypeReference()
        {
        }
    }
}
